package com.qianxun.framework.util.expression;

import com.qianxun.framework.util.ObjectUtil;

import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 表达式工具
 * 表达式规则定义、解析和表达式注入
 *
 * @author: huwei
 * @date: 2019/10/17 9:01
 * @version: 1.0.0
 */
public class ExpressionUtil {
//
//    public static void main(String[] args) {
//        Map<String, Object> map = ObjectUtil.toMap("test", "name");
//        map.put("age", "55");
//
//        UserAccount userAccount = new UserAccount();
//        userAccount.setId(44L);
//        userAccount.setEmail("11@qq.com");
//        map.put("user", userAccount);
//
//        String expression = "#{user.id}@{#sadf3}{##{}#324df{}#{#}#{$}${${%}$#}#{#@}$@#}@$test}sdf@{user.id}aaa}@{}00@{user.email0011";
//        String res = ExpressionUtil.build().parse(expression, map);
//
//        System.err.println(res);
//        System.out.println("over");
//    }

    /**
     * 实例集合，用于存储相同定界符号的实例，避免重复实例化相同对象造成的内存浪费
     */
    private static Map<DelimitedSymbol ,ExpressionUtil> instance = new ConcurrentHashMap<>(5);

    private DelimitedSymbol delimitedSymbol;

    private ExpressionUtil(DelimitedSymbol delimitedSymbol){
        this.delimitedSymbol = delimitedSymbol;
    }

    public static ExpressionUtil build(){
        DelimitedSymbol delimitedSymbol = Configuration.delimitedSymbol;
        return build(delimitedSymbol.getHeadDelimitedSymbol() ,delimitedSymbol.getTailDelimitedSymbol());
    }

    public static ExpressionUtil build(String headDelimited , String tailDelimited){
        DelimitedSymbol delimitedSymbol = new DelimitedSymbol(headDelimited ,tailDelimited);
        ExpressionUtil ins = instance.get(delimitedSymbol);
        if(ins == null){
            ins = new ExpressionUtil(delimitedSymbol);
            instance.put(delimitedSymbol ,ins);
        }
        return ins;
    }

    /**
     * 解析并替换字符串中表达式的原始值
     *
     * @param text 包含表达式的文本内容
     * @param evn  替换表达式值的可选数据源的map映射，其中map的key是与表示式关联的，通过该关联可在map映射中查找到表达式需要替换的值；
     * @return 表达式被替换后的字符串；注意：当dataMap数据源中不存在表达式替换值，则不予替换
     */
    public String parse(String text, Map<String, Object> evn) {
        if (text == null || text.trim().length() == 0) {
            return text;
        }
        ParseObject parseObject = new ParseObject(text);
        do{
            boolean isFind =findExpression(parseObject ,delimitedSymbol);
            if(isFind){
                //copy表达式之前的内容
                copyOriginValueBeforeExpression(parseObject);
                //解析表达式
                String vale = parseExpression(parseObject.getExpression(), evn);
                if(vale == null){
                    String originExpression = parseObject.originExpression();
                    String errorProcessValue = errorProcess(originExpression);
                    parseObject.getParseContent().append(errorProcessValue);
                }else{
                    parseObject.getParseContent().append(vale);
                }
                //偏移越过当前表达式
                parseObject.setOriginOffset(parseObject.getExpressionEndIndex() + parseObject.getTailDelimitedLength());
            }else{
                parseObject.setOver();
            }
        }while (!parseObject.isParseOver());

        return parseObject.parseContent();
    }

    /**
     * TODO 可定义接口，由用户定义错误表达式的解决方案
     * 这里为表达式异常处理：表达式错误，未解析到值，都在该处理方式内容
     * @param originExpression 文本中原始表达式
     * @return 表达式的替换值
     */
    private String errorProcess(String originExpression){
        return originExpression;
    }

    /**
     * 复制表示式之前的原始值到解析文本中
     * @param parseObject
     */
    private void copyOriginValueBeforeExpression(ParseObject parseObject){
        int copyEndIndex = parseObject.getExpressionStartIndex();
        //防止表示式在起始位置带来的异常
        if(parseObject.getOriginOffset() < copyEndIndex){
            String originValue =parseObject.getOriginContent().substring(parseObject.getOriginOffset() ,copyEndIndex);
            parseObject.getParseContent().append(originValue);
            parseObject.setOriginOffset(copyEndIndex);
        }
    }

    private String parseExpression(String expression, Map<String, Object> evn) {
        String[] expressionGroup = splitExpression(expression);
        //不存在层级调用
        if (expressionGroup.length == 0) {
            return evn.get(expression) == null ? null : evn.get(expression).toString();
        }

        Map<String, Object> tempEvn = evn;
        //层级表达式
        for (int i = 0, len = expressionGroup.length; i < len; ++i) {
            String expressionI = expressionGroup[i];
            Object obj = tempEvn.get(expressionI);
            //没有表达式表示的值，跳出，并返回空
            if (obj == null) {
                break;
            }
            //当前表达式还没有抵达最后一个层级，将当前获取的对象转为map，并从新的数据源map中获取表达式值
            if (i + 1 < len) {
                tempEvn = ObjectUtil.toMap(obj);
            } else {
                //以抵达最后一个层级，返回获取对象的toString 表现形式
                return obj.toString();
            }
        }
        return null;
    }

    private boolean findExpression(ParseObject parseObject, DelimitedSymbol delimitedSymbol) {
        String originContent = parseObject.getOriginContent();
        int originOffset = parseObject.getOriginOffset();
        int expressStartIndex = originContent.indexOf(delimitedSymbol.getHeadDelimitedSymbol() ,originOffset);
        if(expressStartIndex == -1){
            parseObject.setExpressionStartIndex(-1).setExpression(null);
            return false;
        }

        int expressEndIndex = originContent.indexOf(delimitedSymbol.tailDelimitedSymbol ,expressStartIndex);
        if(expressEndIndex == -1){
            parseObject.setExpressionEndIndex(-1).setExpression(null);
            return false;
        }

        //截取表达式，并设置表达式下标，及其定界符号长度
        String expression = originContent.substring(expressStartIndex + delimitedSymbol.headDelimitedSymbol.length(), expressEndIndex);
        parseObject.setExpression(expression).setExpressionStartIndex(expressStartIndex).setExpressionEndIndex(expressEndIndex);
        parseObject.setHeadDelimitedLength(delimitedSymbol.headDelimitedSymbol.length());
        parseObject.setTailDelimitedLength(delimitedSymbol.tailDelimitedSymbol.length());
        return true;
    }

    /**
     * 多级变量调用，使用Configuration.CONTACT 设定字符链接
     * @param expression 表达式
     * @return 表达式组
     */
    private String[] splitExpression(String expression){
        return expression.split(Configuration.CONTACT);
    }

    /**
     * 文本解析对象
     */
    private class ParseObject {
        /**
         * 原始内容
         */
        String originContent;
        /**
         * 解析内容
         */
        StringBuffer parseContent;
        /**
         * 当前解析表达式
         */
        String expression;
        /**
         * 表达式在originContent中的起始下标
         */
        int expressionStartIndex;
        /**
         * 头部定界符号长度
         */
        int headDelimitedLength;
        /**
         * 尾部定界符号长度
         */
        int tailDelimitedLength;
        /**
         * 表达式在originContent中的结束下标
         */
        int expressionEndIndex;
        /**
         * 原始内容偏移量，即以检索原始内容的字符数量，下次检索表示式时，需要跳过偏移量的值
         */
        int originOffset;

        ParseObject(String originContent) {
            this.originContent = originContent;
            this.parseContent = new StringBuffer();
        }

        public String getOriginContent() {
            return originContent;
        }

        public ParseObject setOriginContent(String originContent) {
            this.originContent = originContent;
            return this;
        }

        public StringBuffer getParseContent() {
            return parseContent;
        }

        public ParseObject setParseContent(StringBuffer parseContent) {
            this.parseContent = parseContent;
            return this;
        }

        public String getExpression() {
            return expression;
        }

        public ParseObject setExpression(String expression) {
            this.expression = expression;
            return this;
        }

        public int getExpressionStartIndex() {
            return expressionStartIndex;
        }

        public ParseObject setExpressionStartIndex(int expressionStartIndex) {
            this.expressionStartIndex = expressionStartIndex;
            return this;
        }

        public int getHeadDelimitedLength() {
            return headDelimitedLength;
        }

        public ParseObject setHeadDelimitedLength(int headDelimitedLength) {
            this.headDelimitedLength = headDelimitedLength;
            return this;
        }

        public int getTailDelimitedLength() {
            return tailDelimitedLength;
        }

        public ParseObject setTailDelimitedLength(int tailDelimitedLength) {
            this.tailDelimitedLength = tailDelimitedLength;
            return this;
        }

        public int getExpressionEndIndex() {
            return expressionEndIndex;
        }

        public ParseObject setExpressionEndIndex(int expressionEndIndex) {
            this.expressionEndIndex = expressionEndIndex;
            return this;
        }

        public int getOriginOffset() {
            return originOffset;
        }

        public ParseObject setOriginOffset(int originOffset) {
            this.originOffset = originOffset;
            return this;
        }

        boolean isParseOver(){
            return originOffset >= originContent.length();
        }

        String parseContent(){
            return parseContent.toString();
        }

        void setOver() {
            //余下的内容
            String string = originContent.substring(originOffset);
            parseContent.append(string);

            //设置结束
            this.originOffset = originContent.length();
        }

        String originExpression(){
            return originContent.substring(expressionStartIndex ,expressionEndIndex + tailDelimitedLength);
        }
    }

    /**
     * 表达式配置项
     *
     * @author: huwei
     * @date: 2019/10/17 9:04
     * @version: 1.0.0
     */
    private static class Configuration {
        static final String CONTACT = "\\.";
        /**
         * 默认头部定界符
         */
        static final String DEFAULT_HEAD_DELIMITED_SYMBOL = "${";
        /**
         * 默认尾部定界符
         */
        static final String DEFAULT_TAIL_DELIMITED_SYMBOL = "}";

        /**
         * 定界符号
         */
        static DelimitedSymbol delimitedSymbol = new DelimitedSymbol(DEFAULT_HEAD_DELIMITED_SYMBOL ,DEFAULT_TAIL_DELIMITED_SYMBOL);
    }

    private static class DelimitedSymbol {
        /**
         * 头部定界符号
         */
        String headDelimitedSymbol;
        /**
         * 尾部定界符号
         */
        String tailDelimitedSymbol;

        DelimitedSymbol(String headDelimitedSymbol, String tailDelimitedSymbol) {
            this.headDelimitedSymbol = headDelimitedSymbol;
            this.tailDelimitedSymbol = tailDelimitedSymbol;
        }

        public String getHeadDelimitedSymbol() {
            return headDelimitedSymbol;
        }

        public DelimitedSymbol setHeadDelimitedSymbol(String headDelimitedSymbol) {
            this.headDelimitedSymbol = headDelimitedSymbol;
            return this;
        }

        public String getTailDelimitedSymbol() {
            return tailDelimitedSymbol;
        }

        public DelimitedSymbol setTailDelimitedSymbol(String tailDelimitedSymbol) {
            this.tailDelimitedSymbol = tailDelimitedSymbol;
            return this;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            DelimitedSymbol that = (DelimitedSymbol) o;
            return Objects.equals(headDelimitedSymbol, that.headDelimitedSymbol) &&
                    Objects.equals(tailDelimitedSymbol, that.tailDelimitedSymbol);
        }

        @Override
        public int hashCode() {
            return Objects.hash(headDelimitedSymbol, tailDelimitedSymbol);
        }
    }
}
